﻿using BMSPlatform.ViewModel;
using DataManage;
using ServiceLayerManagement;
using System;
using System.Collections.Generic;
using System.Linq;
using static System.Net.Mime.MediaTypeNames;

namespace BMSPlatform.DeviceCommunication
{
    public class BinFileUpgradeCommandDownLoad : Command
    {
        private List<byte> upperCIdBytes = new List<byte>()
        {
            0x18,
            0xA0,
            0x80
        };
        public Dictionary<int, Command> cmdDictionary = new Dictionary<int, Command>()
        {
            { 0, new Command() { Data = new byte[3]{ 0x51,0x01,0xA7 }, Length = 1, Describe = "下位机软件编译日期",
                 VerData = new byte[4]{ 0x00, 0x04, 0xA3, 0x02 } } },
            { 1, new Command() { Data = new byte[3]{ 0x51,0x01,0xA0 }, Length = 1, Describe = "下位机硬件版本号",
                 VerData = new byte[3]{ 0x00, 0x04, 0xA0 } } },
            { 2, new Command() { Data = new byte[3]{ 0x51,0x01,0xA1 }, Length = 1, Describe = "下位机软件版本号",
                 VerData = new byte[3]{ 0x00, 0x04, 0xA1 } } },
            { 3, new Command() { Data = new byte[3]{ 0x51,0x01,0xA2 }, Length = 1, Describe = "BOOT程序软件版本号",
                 VerData = new byte[3]{ 0x00, 0x04, 0xA2 }} },
            { 4, new Command() { Data = new byte[4]{ 0x51,0x02,0xA3,0x01 }, Length = 1, Describe = "BOOT",
                 VerData = new byte[4]{ 0x00, 0x04, 0xA3, 0x02 }} },
            { 5, new Command() { Data = new byte[4]{ 0x51,0x02,0xA3,0x03 }, Length = 1, Describe = "擦除Flash",
                 VerData = new byte[4]{ 0x00, 0x04, 0xA3, 0x04 } } },
            { 6, new Command() { Data = new byte[3]{ 0x51,0x02,0xA5 }, Length = 0, Describe = "发送固件CRC" } }, //其中第三和第四字节是CRC（起始0）
            { 7, new Command() { Data = new byte[4]{ 0x51,0x02,0xA3, 0x05 }, Length = 0, Describe = "发送的大包总数" } },//其中第三和第四字节是大包数（起始0）
            { 8, new Command() { Data = new byte[3]{ 0x51,0x02,0xA6 }, Length = 0, Describe = "发送的单包字节数" } },//其中第三（起始0）
            { 9, new Command() { Data = new byte[3]{ 0x51,0x02,0xA4 }, Length = 0, Describe = "数据烧写" ,
                 VerData = new byte[5]{ 0x00, 0x04, 0xA3, 0x04, 0x00} } },//3：帧序号，4-7：BIN数据
            { 10, new Command() { Data = new byte[4]{ 0x51,0x02,0xA3, 0x08 }, Length = 0, Describe = "CRC校验", 
                VerData = new byte[4]{ 0x00, 0x04, 0xA3, 0x09 } } },
            { 11, new Command() { Data = new byte[4]{ 0x51,0x02,0xA3, 0x0B }, Length = 0, Describe = "使能boot跳入APP" } },

        };

        #region 命令发送
        public void CommanLoad(byte[] binData,byte lowerCIdAdress)
        {
            try
            {
                string str = "";
                //重发次数
                int recount = 0;
                DataFrames dataFrames = new DataFrames()
                {
                    CANType = ZLGCANProtocolType.CAN,
                    CANId = AssemblyUpperCId(lowerCIdAdress, upperCIdBytes),
                    FrameFormat = 1,
                    FrameType = 0,
                };
                for (int i = 0; i < cmdDictionary.Count; i++)
                {
                    if (i > 6 && i < 10)
                    {//进行BIN文件发送
                        BinDataSend(binData, dataFrames);
                        i += 2;
                    }
                    else
                    {//其他指令发送

                        if (i <= 3) str = "获取";
                        if (i == 4) str = "进入";
                        if (i == 5) str = "请求";

                        if (!CommandParsed(DeviceDataHandle.GetResponseData(5 * 1000, cmdDictionary[i].Length, GetDataFrames(dataFrames, i, binData), cmdDictionary[i].VerData), i))
                        {
                            recount++;
                            if (recount > 5) throw new Exception($"{str}{cmdDictionary[i].Describe}失败，停止系统刷写");
                            i--;
                        }
                        else
                        {
                            recount = 0;
                        }
                    }
                }
            }
            catch(Exception ex)
            {
                OnMessageTransEvent($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}：{ex.Message}");
            }
        }

        private void BinDataSend(byte[] binData, DataFrames dataFrames)
        {
            #region 局部变量
            //历史已发的数据索引
            int oldBinDataIndex = 0;
            //当前已发的数据索引
            int binDataIndex = 0;
            //长度
            int len = binData.Length;
            //单包最大数
            byte maxLen = 200;
            //单包帧序号
            byte frameSerialNum = 1;
            //当前分包数
            int dataPacketNum = (len / 200) + (len % 200 == 0 ? 0 : 1);
            //重发此包次数
            int resendCount = 0;
            //烧录验证包
            List<byte> burnVerification = new List<byte>();
            //bin数据发送包
            DataFrames sendDatas;
            //结束当前包数据发送
            bool endCurrentDataSend = false;
            #endregion

            #region 发送数据包
            OnMessageTransEvent($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}：开始烧写数据");
            for (int i = 0; i < dataPacketNum; i++)//遍历大包
            {
                //开始发单帧
                DeviceConnected.SendData(GetDataFrames(dataFrames, 7, binData));
                //发送单帧数
                DeviceConnected.SendData(GetDataFrames(dataFrames, 8, null, i >= (dataPacketNum - 1) ? (byte?)(len - binDataIndex) : maxLen));
                //发送Bin数据包
                for (int j = binDataIndex; j < binData.Length; j+=4)//遍历小包
                {
                    sendDatas = GetDataFrames(dataFrames, 9, binData, null, frameSerialNum, binDataIndex);
                    if (frameSerialNum < 50 && j < binData.Length - 5)
                    {
                        DeviceConnected.SendData(sendDatas);
                    }
                    else if(frameSerialNum >= 50 || j >= binData.Length - 5)
                    {//单包最后一帧等待验证
                        /*
                         * 组装验证包
                         * 1.验证控制指令
                         * 2.验证最后一个包的后三个字节
                         */
                        burnVerification.AddRange(cmdDictionary[9].VerData);
                        burnVerification.AddRange(sendDatas.Data.Skip(5).ToArray());
                        if (!CommandParsed(DeviceDataHandle.GetResponseData(5 * 1000, cmdDictionary[9].Length, sendDatas, burnVerification.ToArray()), 9, sendDatas.Data.Skip(5).ToArray()))
                        {
                            if (resendCount >= 3)
                            {
                                throw new Exception("数据烧写失败多次,停止系统升级");
                            }
                            i--;
                            binDataIndex = oldBinDataIndex;
                            resendCount++;
                        }
                        else
                        {
                            resendCount = 0;//重发置0
                            OnProcessBarEventEvent(1);//进度条更新
                        }
                        endCurrentDataSend = true;
                    }

                    binDataIndex += 4;
                    frameSerialNum++;
                    if (endCurrentDataSend)
                    {
                        if(resendCount <= 0) oldBinDataIndex = binDataIndex;//记录下一次数据包起始位置
                        frameSerialNum = 1;//序号重置
                        endCurrentDataSend = false;
                        burnVerification.Clear();//验证包清空
                        break;
                    }
                   
                }
            }

            OnMessageTransEvent($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}：数据烧写完毕");
            #endregion
        }

        /// <summary>
        /// 解析发送指令
        /// </summary>
        /// <param name="singlePackageNum">单包所含帧数</param>
        /// <param name="serialNo">小包序号</param>
        private DataFrames GetDataFrames(DataFrames dataFrames, int index, byte[] binData = null, byte? singlePackageNum = null, byte? serialNo = null,int? dataIndex = null)
        {
            List<byte> bytes = new List<byte>();
            bytes.AddRange(cmdDictionary[index].Data);
            switch(index)
            {
                case 6://发送CRC
                    bytes.AddRange(binData.Skip(27).Take(2).ToArray());
                    break;
                case 8://发送单包数
                    bytes.Add((byte)singlePackageNum);
                    break;
                case 9://发送数据包
                    bytes.Add((byte)serialNo);
                    if (dataIndex + 4 <= binData.Length)
                    {
                        bytes.AddRange(binData.Skip((int)dataIndex).Take(4).ToArray());
                    }
                    else
                    {
                        bytes.AddRange(binData.Skip((int)dataIndex).ToArray());
                    }
                    break;
                default:
                    break;
            }
            
            dataFrames.Data = CompleteArray(bytes.ToArray(), 8);
            dataFrames.Length = (byte)dataFrames.Data.Length;
            return dataFrames;
        }
        #endregion

        /// <summary>
        /// 应答回复解析
        /// </summary>
        /// <param name="respResult">下位机回复</param>
        public bool CommandParsed(RespResult<List<DataFrames>> respResult, int index, byte[] sendData = null)
        {
            string text = "";
            List<byte> verDataList = new List<byte>();
            bool verificationPass = true;//验证通过
            byte controlCmd = 0;//上位机发送的控制指令
            byte controlType = cmdDictionary[index].Data[2];//上位机发送的控制指令类型
            if(cmdDictionary[index].Data.Length > 3) controlCmd = cmdDictionary[index].Data[3];

            if (respResult.Code != 0)
            {//超时验证
                verificationPass = false;
            }
            if (verificationPass && respResult.Body?.Data?.Count > 0)
            {//应答包验证
                DataFrames dataFrames = respResult.Body.Data.FirstOrDefault();
                if (cmdDictionary[index].VerData?.Length > 0)
                {
                    verDataList.AddRange(cmdDictionary[index].VerData);
                    if (sendData?.Length > 0) verDataList.AddRange(sendData);
                    for (int i = 0; i < verDataList.Count; i++)
                    {
                        if (verDataList[i] != dataFrames.Data[i]) verificationPass = false;
                    }
                }
            }
            text = $"{cmdDictionary[index].Describe}";
            switch (controlType)
            {
                case 0xA0:
                case 0xA1:
                case 0xA2:
                case 0xA7:
                    text = verificationPass ? $"{text}获取成功" : "";
                    break;
                case 0xA3:
                    if (controlCmd == 0x01)
                    {
                        text = verificationPass ? $"进入{text}程序" : $"{respResult.Message},进入{text}程序失败";
                    }
                    else if (controlCmd == 0x03)
                    {
                        text = verificationPass ? $"{text}完毕" : $"{respResult.Message},{text}失败";
                    }
                    else if (controlCmd == 0x08)
                    {
                        text = verificationPass ? $"下位机{text}通过" : $"{respResult.Message},下位机{text}失败";
                    }
                    break;
                case 0xA4:
                    text = verificationPass ? "" : $"{respResult.Message},{text}失败，重新发送该包";
                    break;           
            }
            if(!string.IsNullOrWhiteSpace(text)) OnMessageTransEvent($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}：{text}");
            return verificationPass;
        }
    }
}
